package token

import (
	"context"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/ethclient"
	"github.com/xinliangnote/go-gin-api/configs"
	"github.com/xinliangnote/go-gin-api/internal/contract"
	"github.com/xinliangnote/go-gin-api/internal/contract/evmAbi"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
	"math/big"
)

func NewModel() *Token {
	return new(Token)
}

func (t *Token) Create(db *mongo.Client) error {
	cfg := configs.Get().Mongodb
	tokenClient := db.Database(cfg.Config.Name).Collection("token")
	_, err := tokenClient.InsertOne(context.TODO(), t)
	return err
}

func (t *Token) GetInfoByAddress(db *mongo.Client) (Token, error) {
	var token Token
	cfg := configs.Get().Mongodb
	tokenClient := db.Database(cfg.Config.Name).Collection("token")
	err := tokenClient.FindOne(context.TODO(), bson.D{{"address", t.Address}}).Decode(&token)
	return token, err
}

func (t *Token) Delete(db *mongo.Client) error {
	cfg := configs.Get().Mongodb
	tokenClient := db.Database(cfg.Config.Name).Collection("token")
	_, err := tokenClient.DeleteOne(context.Background(), bson.D{{"_id", t.ID}})
	return err
}

func (t *Token) Update(db *mongo.Client) error {
	cfg := configs.Get().Mongodb
	tokenClient := db.Database(cfg.Config.Name).Collection("token")
	update := bson.M{"$set": bson.M{
		"chainName": t.ChainName,
		"chainId":   t.ChainId,
		"address":   t.Address,
		"name":      t.Name,
		"symbol":    t.Symbol,
		"decimal":   t.Decimals,
	}}
	_, err := tokenClient.UpdateOne(context.Background(), bson.D{{"_id", t.ID}}, update)
	return err
}

func (t *Token) List(db *mongo.Client, pageIndex uint, pageSize uint) ([]Token, error) {
	var tokenList []Token
	cfg := configs.Get().Mongodb
	tokenClient := db.Database(cfg.Config.Name).Collection("token")
	skip := int64((pageIndex - 1) * pageSize)
	findOptions := options.Find()
	findOptions.SetSort(bson.D{{"_id", -1}})
	findOptions.SetSkip(skip)
	findOptions.SetLimit(int64(pageSize))

	filter := bson.M{}
	if t.Address != "" {
		filter["address"] = t.Address
	}
	if t.Symbol != "" {
		filter["symbol"] = t.Symbol
	}
	if t.ChainName != "" {
		filter["chainName"] = t.ChainName
	}
	if t.ChainId != 0 {
		filter["chainId"] = t.ChainId
	}
	find, err := tokenClient.Find(context.TODO(), filter, findOptions)
	if err != nil {
		return nil, err
	}
	err = find.All(context.TODO(), &tokenList)

	return tokenList, err

}

func (t *Token) Count(db *mongo.Client) (int64, error) {
	cfg := configs.Get().Mongodb
	tokenClient := db.Database(cfg.Config.Name).Collection("token")
	documents, err := tokenClient.CountDocuments(context.TODO(), bson.D{})
	return documents, err
}

func ParseTokenAddress(tokenAddress string, client *ethclient.Client, token *Token) error {
	tokenName, err := contract.CallContractMethod(client, common.HexToAddress(tokenAddress), evmAbi.UNISWAP_V2_PAIR_ABI, "name")
	if err != nil {
		return err
	}
	tokenSym, err := contract.CallContractMethod(client, common.HexToAddress(tokenAddress), evmAbi.UNISWAP_V2_PAIR_ABI, "symbol")
	if err != nil {
		return err
	}
	tokenDec, err := contract.CallContractMethod(client, common.HexToAddress(tokenAddress), evmAbi.UNISWAP_V2_PAIR_ABI, "decimals")
	if err != nil {
		return err
	}
	tokenTot, err := contract.CallContractMethod(client, common.HexToAddress(tokenAddress), evmAbi.UNISWAP_V2_PAIR_ABI, "totalSupply")
	if err != nil {
		return err
	}
	token.Name = tokenName[0].(string)
	token.Symbol = tokenSym[0].(string)
	token.Decimals = uint8(uint(tokenDec[0].(uint8)))
	token.TotalSupply = tokenTot[0].(*big.Int).String()
	return nil
}

func (t *Token) GetInfoById(db *mongo.Client) (Token, error) {
	var token Token
	cfg := configs.Get().Mongodb
	tokenClient := db.Database(cfg.Config.Name).Collection("token")
	err := tokenClient.FindOne(context.TODO(), bson.D{{"_id", t.ID}}).Decode(&token)
	return token, err
}
